1 module hip.timer;
2 public import hip.api.systems.timer;
3 
4 class HipTimer : IHipTimer
5 {
6     ///Enforce naming for making debugging easier
7     string name;
8     protected uint loopCount = 0;
9     protected bool loops = false;
10     protected float deltaTime = 0;
11     protected float accumulator = 0;
12     protected float durationSeconds = 0;
13     protected bool isRunning = false;
14     protected void delegate(float progress, uint loopCount)[] handlers;
15     protected HipTimerType type;
16 
17     /**
18     *   Call `addHandler` after creation for adding what to do
19     */
20     this(string name, float durationSeconds, HipTimerType type = HipTimerType.oneShot, bool loops = false)
21     {
22         this.setProperties(name, durationSeconds, type, loops);
23     }
24     /**
25     *   Perfect function for making a timer pool
26     */
27     void setProperties(string name, float durationSeconds, HipTimerType type, bool loops)
28     {
29         this.name = name;
30         this.durationSeconds = durationSeconds;
31         this.type = type;
32         this.loops = loops;
33         assert(type == HipTimerType.oneShot || type == HipTimerType.progressive, "Invalid timer type");
34         isRunning = false;
35         accumulator = 0;
36         loopCount = 0;
37     }
38     string getName(){return name;}
39     float getDuration(){return durationSeconds;}
40     float getProgress()
41     {
42         if(durationSeconds == 0 || accumulator >= durationSeconds)
43             return 1.0;
44         return accumulator/durationSeconds;
45     }
46     HipTimer addHandler(void delegate() handler)
47     {
48         handlers~=(prog, count){handler();};
49         return this;
50     }
51     HipTimer addHandler(void delegate(float progress) handler)
52     {
53         handlers~=(prog, count){handler(prog);};
54         return this;
55     }
56     HipTimer addHandler(void delegate(float progress, uint loopCount) handler)
57     {
58         handlers~=handler;
59         return this;
60     }
61     void forceFinish()
62     {
63         foreach (h; handlers) h(1, loopCount);
64         stop();
65     }
66 
67     void pause(){isRunning = false;}
68     HipTimer play()
69     {
70         if(durationSeconds == 0)
71             forceFinish();
72         else
73             isRunning = true;
74         return this;
75     }
76     void stop()
77     {
78         isRunning = false;
79         accumulator = 0;
80         loopCount = 0;
81     }
82     void reset()
83     {
84         stop();
85         play();
86     }
87     void loopRestart()
88     {
89         loopCount++;
90         accumulator = 0;
91     }
92 
93     private void executeHandlers()
94     {
95         foreach(h;handlers)h(getProgress(), loopCount);
96     }
97     private bool checkLoops()
98     {
99         if(loops)
100         {
101             loopRestart();
102             return false;
103         }
104         stop();
105         return true;
106     }
107 
108 
109     ///Returns wether it has finished
110     bool tick (float dt)
111     {
112         if(isRunning)
113         {
114             this.deltaTime = dt;
115             accumulator+= dt;
116             switch(type)
117             {
118                 case HipTimerType.oneShot:
119                     if(accumulator >= durationSeconds)
120                     {
121                         executeHandlers();
122                         return checkLoops();
123                     }
124                     break;
125                 case HipTimerType.progressive:
126                     executeHandlers();
127                     if(accumulator >= durationSeconds)
128                         return checkLoops();
129                     break;
130                 default:break;
131             }
132         }
133         return false;
134     }
135 }
136 
137 class HipSequence : HipTimer, IHipTimerList
138 {
139     protected IHipTimer[] timerList;
140     protected uint listCursor = 0;
141     protected float cursorDuration = 0;
142     protected float listAccumulator = 0;
143     protected void delegate()[] onFinishList;
144 
145     this(string name = "Sequence", scope IHipTimer[] timers = []...)
146     {
147         super(name, 0, HipTimerType.progressive);
148         foreach(t;timers)
149         {
150             timerList~= t;
151         }
152         cursorDuration = timerList[0].getDuration();
153         recalculateDuration();
154 
155         addHandler((prog, count)
156         {
157             if(accumulator - listAccumulator >= cursorDuration)
158             {
159                 if(listCursor + 1 < timerList.length)
160                 {
161                     //Guarantee that it finishes here
162                     timerList[listCursor].tick(deltaTime);
163                     cursorDuration = timerList[++listCursor].getDuration();
164                     timerList[listCursor].play();
165                     listAccumulator+= cursorDuration;
166                 }
167             }
168             timerList[listCursor].tick(deltaTime);
169         });
170     }
171     override HipSequence play()
172     {
173         super.play();
174         timerList[0].play();
175         return this;
176     }
177     override void stop()
178     {
179         super.stop();
180         foreach(onFinish; onFinishList)
181             onFinish();
182     }
183 
184     void recalculateDuration()
185     {
186         foreach(t;timerList)
187             durationSeconds+= t.getDuration();
188         setProperties(this.name, durationSeconds, HipTimerType.progressive, false);
189     }
190     HipSequence add(IHipTimer timer)
191     {
192         if(cursorDuration == 0)
193             cursorDuration = timer.getDuration();
194         timerList~= timer;
195         recalculateDuration();
196         return this;
197     }
198     HipSequence addOnFinish(void delegate() onFinish)
199     {
200         onFinishList~= onFinish;
201         return this;
202     }
203 }
204 
205 class HipSpawn : HipTimer, IHipTimerList
206 {
207     protected IHipTimer[] timerList;
208     protected void delegate()[] onFinishList;
209 
210     /**
211     *   Will receive a list of timers and update them at the same time.
212     *   Total duration is equal to the max timer duration.
213     *   Call `.play()` after `new HipSpawn`, it is necessary for the timers
214     *   to be updated.
215     */
216     this(string name = "Spawn", scope IHipTimer[] timers = []...)
217     {
218         super(name, 0, HipTimerType.progressive);
219         foreach(t;timers)
220         {
221             timerList~= t;
222         }
223         recalculateDuration();
224         addHandler((prog, count)
225         {
226             foreach(t; timerList)
227                 t.tick(deltaTime);
228         });
229     }
230     override HipSpawn play()
231     {
232         super.play();
233         foreach(t; timerList)
234         {
235             t.play();
236         }
237         return this;
238     }
239     protected void recalculateDuration()
240     {
241         float durationSeconds = 0;
242         foreach(t;timerList)
243         {
244             if(t.getDuration() > durationSeconds)
245                 durationSeconds = t.getDuration();
246         }
247         setProperties(this.name, durationSeconds, HipTimerType.progressive, false);
248     }
249 
250     override void stop()
251     {
252         super.stop();
253         foreach(onFinish; onFinishList)
254             onFinish();
255     }
256 
257     HipSpawn add(IHipTimer timer)
258     {
259         timerList~= timer;
260         recalculateDuration();
261         return this;
262     }
263 
264     HipSpawn addOnFinish(void delegate() onFinish)
265     {
266         onFinishList~= onFinish;
267         return this;
268     }
269 }